﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using BobcatMultiTouch;
using MouseKeyboardLibrary;
using System.Drawing;
using BobcatUtility;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;

namespace MultiTouchSource_Mouse
{
    /// <summary>
    /// This class implements the IMultiTouchSource interface to provide a source
    /// that raises events when mouse events occur.
    /// </summary><remarks>
    /// It uses the MouseKeyboardLibrary from 'CodeSummoner' at:
    /// http://www.codeproject.com/KB/system/globalmousekeyboardlib.aspx
    /// </remarks>
    public class MouseSource : IMultiTouchSource
    {
        #region Events

        // Events from the IMultiTouchSource interface...
        public event EventHandler<TouchArgs> TouchDownEvent;
        public event EventHandler<TouchArgs> TouchUpEvent;
        public event EventHandler<TouchArgs> TouchMoveEvent;
        public event EventHandler<ConfigChangedArgs> ConfigChangedEvent;

        #endregion

        #region Public methods

        /// <summary>
        /// Constructor.
        /// </summary>
        public MouseSource()
        {
        }

        /// <summary>
        /// Called to shut down the source.
        /// </summary><remarks>
        /// (From the IMultiTouchSource interface.) 
        /// </remarks>
        public void close()
        {
            // We stop listening for mouse events...
            if (m_MouseHook == null)
            {
                // The source was never initialized...
                return;
            }
            m_MouseHook.MouseDown -= onMouseDown;
            m_MouseHook.MouseUp -= onMouseUp;
            m_MouseHook.MouseMove -= onMouseMove;
            m_MouseHook.MouseWheel -= onMouseWheel;
            m_MouseHook.Stop();
            m_MouseHook = null;
        }


        /// <summary>
        /// We set up the source using the MouseKeyboardLibrary.
        /// </summary><remarks>
        /// (From the IMultiTouchSource interface.) 
        /// </remarks>
        public void initializeFromXMLConfig(string strXML)
        {
            m_MouseHook = new MouseHook();

            // We ignore the XML, as there is no special config for the mouse.
            // We register for events from the mouse...
            m_MouseHook.MouseDown += onMouseDown;
            m_MouseHook.MouseUp += onMouseUp;
            m_MouseHook.MouseMove += onMouseMove;
            m_MouseHook.MouseWheel += onMouseWheel;
            m_MouseHook.Start();
        }

        /// <summary>
        /// Returns the XML needed to configure the source. For the mouse no config is 
        /// needed, so we return an empy string.
        /// </summary>
        public string getXMLConfig()
        {
            return "";
        }

        /// <summary>
        /// Returns the source name. 
        /// </summary><remarks>
        /// (From the IMultiTouchSource interface.) 
        /// </remarks>
        public string getSourceName()
        {
            return "Mouse";
        }


        /// <summary>
        /// Returns the number of points supported by this source. 
        /// </summary>
        public int getMaxNumberOfPoints()
        {
            return 1;
        }

        /// <summary>
        /// Returns the icon for this source.
        /// </summary>
        public Image getIcon()
        {
            return Utility.getImageFromResources("mouse.png");
        }

        /// <summary>
        /// Returns null as there is no special config for the mouse.
        /// </summary>
        public UserControl getConfigControl()
        {
            return null;
        }

        #endregion

        #region Private functions

        /// <summary>
        /// Called when we get a mouse-down event. We raise a touch event to our observers.
        /// </summary>
        private void onMouseDown(object sender, MouseEventArgs args)
        {
            if (TouchDownEvent != null)
            {
                TouchDownEvent(this, getTouchArgsFromMouseArgs(args));
            }
        }

        /// <summary>
        /// Called when we get a mouse-up event. We raise the equivalent touch event.
        /// </summary>
        private void onMouseUp(object sender, MouseEventArgs args)
        {
            if (TouchUpEvent != null)
            {
                TouchUpEvent(this, getTouchArgsFromMouseArgs(args));
            }
        }

        /// <summary>
        /// Called when we get a mouse-move event. We raise a touch-move event if the 
        /// left-button is pressed.
        /// </summary>
        private void onMouseMove(object sender, MouseEventArgs args)
        {
            short keyState = GetAsyncKeyState(VK_LBUTTON);
            bool bLeftButtonPressed = (keyState & 0x8000) != 0;
            if (bLeftButtonPressed == true && TouchMoveEvent != null)
            {
                TouchMoveEvent(this, getTouchArgsFromMouseArgs(args));
            }
        }
        
        private void onMouseWheel(object sender, MouseEventArgs args)
        {
        }


        /// <summary>
        /// We create and fill in the TouchArgs structure that we return with our events from
        /// a mouse-events structure.
        /// </summary>
        private TouchArgs getTouchArgsFromMouseArgs(MouseEventArgs args)
        {
            TouchArgs result = new TouchArgs();
            result.Button = convertButtonType(args.Button);
            result.PointIndex = 0;
            result.ScreenPosition = Utility.copy(args.Location);
            result.Pressure = 1.0;
            return result;
        }


        /// <summary>
        /// We convert a MouseButtons enum that we get from a mouse event\
        /// into the button-type needed for the events we fire.
        /// </summary>
        private TouchArgs.ButtonType convertButtonType(MouseButtons button)
        {
            TouchArgs.ButtonType eResult;
            switch (button)
            {
                case MouseButtons.Left:
                    eResult = TouchArgs.ButtonType.LEFT;
                    break;

                case MouseButtons.Right:
                    eResult = TouchArgs.ButtonType.RIGHT;
                    break;

                case MouseButtons.Middle:
                    eResult = TouchArgs.ButtonType.MIDDLE;
                    break;

                default:
                    eResult = TouchArgs.ButtonType.NONE;
                    break;
            }
            return eResult;
        }

        #endregion

        #region User32 imports

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern short GetAsyncKeyState(int keyCode);

        private const int VK_LBUTTON = 1;

        #endregion

        #region Private data

        // The mouse object from the MouseKeyboardLibrary...
        private MouseHook m_MouseHook = null;

        #endregion
    }
}
